package com.zzh.partnersys.ai.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzh.partnersys.ai.entity.AiAssistantDO;
import com.zzh.partnersys.ai.entity.AiAssistantDocumentDO;
import com.zzh.partnersys.ai.mapper.AiAssistantDocumentMapper;
import com.zzh.partnersys.ai.mapper.AiAssistantMapper;
import com.zzh.partnersys.ai.service.AiAssistantDocumentService;
import com.zzh.partnersys.ai.service.AiAssistantService;
import com.zzh.partnersys.ai.util.FileStorageUtil;
import com.zzh.partnersys.ai.util.RedisCacheUtil;
import com.zzh.partnersys.common.exception.BusinessException;
import com.zzh.partnersys.common.exception.ResultCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import jakarta.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * AI助手文档关联表 服务实现类
 * </p>
 *
 * @author zzh
 * @since 2025-01-XX
 */
@Slf4j
@Service
public class AiAssistantDocumentServiceImpl extends ServiceImpl<AiAssistantDocumentMapper, AiAssistantDocumentDO> implements AiAssistantDocumentService {

    @Resource
    private RedisCacheUtil redisCacheUtil;

    @Resource
    private FileStorageUtil fileStorageUtil;

    @Resource
    private AiAssistantMapper aiAssistantMapper;

    /**
     * Redis缓存Key前缀
     */
    private static final String CACHE_KEY_PREFIX = "ai:document:";
    private static final String ASSISTANT_DOCUMENTS_KEY_PREFIX = "ai:document:assistant:";
    private static final long CACHE_EXPIRE_TIME = 24; // 缓存过期时间（小时）

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AiAssistantDocumentDO uploadDocument(Long assistantId, Long userId, MultipartFile file) {
        // 1. 参数校验
        if (assistantId == null || assistantId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        if (userId == null || userId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        if (file == null || file.isEmpty()) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        // 2. 校验助手是否存在且属于该用户
        AiAssistantDO assistant = aiAssistantMapper.selectById(assistantId);
        if (assistant == null) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        if (!assistant.getUserId().equals(userId)) {
            throw new BusinessException(ResultCodeEnum.NO_AUTH);
        }

        // 3. 上传文件
        String documentUrl;
        try {
            documentUrl = fileStorageUtil.uploadFile(file);
        } catch (IOException e) {
            log.error("文件上传失败，assistantId: {}, userId: {}", assistantId, userId, e);
            throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
        }

        // 4. 创建文档记录
        AiAssistantDocumentDO document = new AiAssistantDocumentDO();
        document.setAssistantId(assistantId);
        document.setDocumentName(file.getOriginalFilename());
        document.setDocumentUrl(documentUrl);
//        document.setDocumentSize(fileStorageUtil.getFileSize(file));
        document.setDocumentType(fileStorageUtil.getFileType(file));
        document.setUploadTime(LocalDateTime.now());
        document.setStatus(1); // 默认有效
        document.setIsDelete(0);

        // 5. 保存到数据库
        boolean saved = this.save(document);
        if (!saved) {
            // 如果保存失败，删除已上传的文件
            fileStorageUtil.deleteFile(documentUrl);
            throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
        }

        // 6. 清除助手文档列表缓存
        clearAssistantDocumentsCache(assistantId);

        log.info("上传文档成功，documentId: {}, assistantId: {}, userId: {}", document.getId(), assistantId, userId);
        return document;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<AiAssistantDocumentDO> batchUploadDocuments(Long assistantId, Long userId, List<MultipartFile> files) {
        // 1. 参数校验
        if (assistantId == null || assistantId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        if (userId == null || userId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        if (files == null || files.isEmpty()) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        // 2. 校验助手是否存在且属于该用户
        AiAssistantDO assistant = aiAssistantMapper.selectById(assistantId);
        if (assistant == null) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        if (!assistant.getUserId().equals(userId)) {
            throw new BusinessException(ResultCodeEnum.NO_AUTH);
        }

        // 3. 批量上传文件
        List<AiAssistantDocumentDO> documents = new ArrayList<>();
        List<String> uploadedUrls = new ArrayList<>(); // 用于失败时回滚

        try {
            for (MultipartFile file : files) {
                if (file == null || file.isEmpty()) {
                    continue;
                }

                // 上传文件
                String documentUrl;
                try {
                    documentUrl = fileStorageUtil.uploadFile(file);
                    uploadedUrls.add(documentUrl);
                } catch (IOException e) {
                    log.error("批量上传文件失败，assistantId: {}, fileName: {}", assistantId, file.getOriginalFilename(), e);
                    // 回滚已上传的文件
                    for (String url : uploadedUrls) {
                        fileStorageUtil.deleteFile(url);
                    }
                    throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
                }

                // 创建文档记录
                AiAssistantDocumentDO document = new AiAssistantDocumentDO();
                document.setAssistantId(assistantId);
                document.setDocumentName(file.getOriginalFilename());
                document.setDocumentUrl(documentUrl);
//                document.setDocumentSize(fileStorageUtil.getFileSize(file));
                document.setDocumentType(fileStorageUtil.getFileType(file));
                document.setUploadTime(LocalDateTime.now());
                document.setStatus(1);
                document.setIsDelete(0);

                documents.add(document);
            }

            // 4. 批量保存到数据库
            if (!documents.isEmpty()) {
                boolean saved = this.saveBatch(documents);
                if (!saved) {
                    // 如果保存失败，删除已上传的文件
                    for (String url : uploadedUrls) {
                        fileStorageUtil.deleteFile(url);
                    }
                    throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
                }
            }

            // 5. 清除助手文档列表缓存
            clearAssistantDocumentsCache(assistantId);

            log.info("批量上传文档成功，assistantId: {}, userId: {}, count: {}", assistantId, userId, documents.size());
            return documents;

        } catch (Exception e) {
            // 发生异常时，删除已上传的文件
            for (String url : uploadedUrls) {
                fileStorageUtil.deleteFile(url);
            }
            throw e;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteDocument(Long documentId, Long userId) {
        // 1. 参数校验
        if (documentId == null || documentId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        if (userId == null || userId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        // 2. 查询文档并校验权限
        AiAssistantDocumentDO document = this.getById(documentId);
        if (document == null) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        // 通过助手校验权限
        AiAssistantDO assistant = aiAssistantMapper.selectById(document.getAssistantId());
        if (assistant == null || !assistant.getUserId().equals(userId)) {
            throw new BusinessException(ResultCodeEnum.NO_AUTH);
        }

        // 3. 逻辑删除文档
        boolean deleted = this.removeById(documentId);
        if (!deleted) {
            throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
        }

        // 4. 删除物理文件（可选，根据业务需求决定是否删除）
        // fileStorageUtil.deleteFile(document.getDocumentUrl());

        // 5. 清除缓存
        clearDocumentCache(documentId);
        clearAssistantDocumentsCache(document.getAssistantId());

        log.info("删除文档成功，documentId: {}, userId: {}", documentId, userId);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchDeleteDocuments(List<Long> documentIds, Long userId) {
        // 1. 参数校验
        if (documentIds == null || documentIds.isEmpty()) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        if (userId == null || userId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        // 2. 查询所有文档并校验权限
        List<AiAssistantDocumentDO> documents = this.listByIds(documentIds);
        if (documents.isEmpty()) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        // 校验所有文档都属于该用户
        for (AiAssistantDocumentDO document : documents) {
            AiAssistantDO assistant = aiAssistantMapper.selectById(document.getAssistantId());
            if (assistant == null || !assistant.getUserId().equals(userId)) {
                throw new BusinessException(ResultCodeEnum.NO_AUTH);
            }
        }

        // 3. 批量逻辑删除
        boolean deleted = this.removeByIds(documentIds);
        if (!deleted) {
            throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
        }

        // 4. 清除缓存
        for (AiAssistantDocumentDO document : documents) {
            clearDocumentCache(document.getId());
            clearAssistantDocumentsCache(document.getAssistantId());
        }

        log.info("批量删除文档成功，documentIds: {}, userId: {}", documentIds, userId);
        return true;
    }

    @Override
    public List<AiAssistantDocumentDO> getAssistantDocuments(Long assistantId) {
        if (assistantId == null || assistantId <= 0) {
            return null;
        }

        // 1. 先从Redis缓存获取
        String cacheKey = ASSISTANT_DOCUMENTS_KEY_PREFIX + assistantId;
        List<AiAssistantDocumentDO> cachedDocuments = redisCacheUtil.getCacheList(cacheKey);
        if (cachedDocuments != null && !cachedDocuments.isEmpty()) {
            log.debug("从缓存获取助手文档列表，assistantId: {}", assistantId);
            return cachedDocuments;
        }

        // 2. 从数据库查询
        List<AiAssistantDocumentDO> documents = this.list(
                new LambdaQueryWrapper<AiAssistantDocumentDO>()
                        .eq(AiAssistantDocumentDO::getAssistantId, assistantId)
                        .eq(AiAssistantDocumentDO::getIsDelete, 0)
                        .eq(AiAssistantDocumentDO::getStatus, 1) // 只查询有效的文档
                        .orderByDesc(AiAssistantDocumentDO::getUploadTime)
        );

        // 3. 存入缓存
        if (documents != null && !documents.isEmpty()) {
            redisCacheUtil.setCacheList(cacheKey, documents, (int) CACHE_EXPIRE_TIME, TimeUnit.HOURS);
            log.debug("助手文档列表已缓存，assistantId: {}, count: {}", assistantId, documents.size());
        }

        return documents;
    }

    @Override
    public AiAssistantDocumentDO getDocumentById(Long documentId) {
        if (documentId == null || documentId <= 0) {
            return null;
        }

        // 1. 先从Redis缓存获取
        String cacheKey = CACHE_KEY_PREFIX + documentId;
        AiAssistantDocumentDO cachedDocument = redisCacheUtil.getCacheObject(cacheKey);
        if (cachedDocument != null) {
            log.debug("从缓存获取文档，documentId: {}", documentId);
            return cachedDocument;
        }

        // 2. 从数据库查询
        AiAssistantDocumentDO document = this.getById(documentId);
        if (document == null || document.getIsDelete() == 1) {
            return null;
        }

        // 3. 存入缓存
        if (document != null) {
            redisCacheUtil.setCacheObject(cacheKey, document, (int) CACHE_EXPIRE_TIME, TimeUnit.HOURS);
            log.debug("文档信息已缓存，documentId: {}", documentId);
        }

        return document;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateDocumentStatus(Long documentId, Long userId, Integer status) {
        // 1. 参数校验
        if (documentId == null || documentId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        if (userId == null || userId <= 0) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        if (status == null || (status != 0 && status != 1)) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        // 2. 查询文档并校验权限
        AiAssistantDocumentDO document = this.getById(documentId);
        if (document == null) {
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        // 通过助手校验权限
        AiAssistantDO assistant = aiAssistantMapper.selectById(document.getAssistantId());
        if (assistant == null || !assistant.getUserId().equals(userId)) {
            throw new BusinessException(ResultCodeEnum.NO_AUTH);
        }

        // 3. 更新状态
        document.setStatus(status);
        boolean updated = this.updateById(document);
        if (!updated) {
            throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
        }

        // 4. 清除缓存
        clearDocumentCache(documentId);
        clearAssistantDocumentsCache(document.getAssistantId());

        log.info("更新文档状态成功，documentId: {}, userId: {}, status: {}", documentId, userId, status);
        return true;
    }

    /**
     * 清除文档缓存
     */
    private void clearDocumentCache(Long documentId) {
        String cacheKey = CACHE_KEY_PREFIX + documentId;
        redisCacheUtil.deleteObject(cacheKey);
    }

    /**
     * 清除助手文档列表缓存
     */
    private void clearAssistantDocumentsCache(Long assistantId) {
        String cacheKey = ASSISTANT_DOCUMENTS_KEY_PREFIX + assistantId;
        redisCacheUtil.deleteObject(cacheKey);
    }
}

